package org.example.mqtexxt.mqserver.datacenter;

/*
通过这个类来对硬盘上的消息进行管理
 */

import com.sun.source.tree.BindingPatternTree;
import org.example.mqtexxt.common.BinaryTool;
import org.example.mqtexxt.common.MqException;
import org.example.mqtexxt.mqserver.core.Binding;
import org.example.mqtexxt.mqserver.core.MSGQueue;
import org.example.mqtexxt.mqserver.core.Message;

import java.io.*;
import java.text.FieldPosition;
import java.util.LinkedList;
import java.util.RandomAccess;
import java.util.Scanner;

public class MessageFileManager {

    //定义一个内部类表示统计信息
   static public class Stat{
       public int totalCount;
       public int validCount;
    }

    //这个方法获取到指定队列的对应消息文件所在的路径
   private String getQueueDir(String queueName){
       return "./data/"+queueName;
   }


    //这个方法用来获取到该队列的消息数据文件所在的路径
   private String getQueueDataPath(String queueName){
       return getQueueDir(queueName) + "/queue_data.txt";
   }

    //这个方法用来获取到该队列的消息统计完文件所在的路径
    private String getQueueStatPath(String queueName){
       return getQueueDir(queueName) + "/queue_stat.txt";
    }


    //读取消息中统计文件的方法
   private  Stat readStat(String queueName) throws FileNotFoundException {
       Stat stat1 = new Stat();
       InputStream inputStream =new FileInputStream(getQueueStatPath(queueName));
       Scanner scanner = new Scanner(inputStream);
       stat1.totalCount = scanner.nextInt();
       stat1.validCount = scanner.nextInt();
       return stat1;
   }

   //这个方法是用来写消息统计文件的方法
   private void writeStat(String queueName, Stat stat) throws FileNotFoundException {
       OutputStream outputStream = new FileOutputStream(getQueueStatPath(queueName));
       PrintWriter printWriter = new PrintWriter(outputStream);
       printWriter.write(stat.totalCount + "/t"+ stat.validCount);
       printWriter.flush();
   }

   //创建队列对应的消息目录和文件
   private void createQueueFile(String queueName) throws IOException {
       //1.创建队列对应的目录
       //2.创建消息数据文件
       //3.创建消息统计文件
       //4. 给消息的统计文件设置一个初始值
       File baseDir = new File(getQueueDir(queueName));
       if(!baseDir.exists()){
           boolean ok1 = baseDir.mkdirs();
           if(!ok1){
               throw new IOException("创建队列的对应目录失败：baseDir="+baseDir.getAbsolutePath());
           }
       }

       //创建消息数据文件
       File queueDataFile = new File(getQueueDataPath(queueName));
       if(!queueDataFile.exists()){
           boolean ok2 = queueDataFile.createNewFile();
           if(!ok2){
               throw new IOException("创建消息数据文件失败呢: queueDataFile=" + queueDataFile.getAbsolutePath());
           }
       }

       //创建消息统计文件
       File queueStatFile = new File(getQueueStatPath(queueName));
       if(!queueStatFile.exists()){
           boolean ok3 = queueStatFile.createNewFile();
           if(!ok3){
               throw new IOException("创建消息统计文件失败呢 queueStatFile="+queueStatFile.getAbsolutePath());
           }
       }
       //给消息统计文件设置一个初始值
       Stat stat = new Stat();
       stat.totalCount = 0;
       stat.validCount = 0;
       writeStat(queueName,stat);
   }

   //删除队列的目录和文件
    private void deleteQueueFiles(String queueName) throws IOException {
       //先删除文件，在删除目录
        //1. 删除数据文件
        //2. 删除统计文件
        //3. 删除队列的目录
        //4. 检测是否删除成功了

        //删除数据文件
        File queueDataFile = new File(getQueueDataPath(queueName));
        boolean ok1 = queueDataFile.delete();

        //删除统计文件
        File queueStatFile = new File(getQueueStatPath(queueName));
        boolean ok2 = queueStatFile.delete();

        //删除目录
        File baseDir = new File(getQueueDir(queueName));
        boolean ok3 = baseDir.delete();
        if(!ok1 || !ok2 || !ok3){
            //有任意一个删除失败，抛出异常
            throw new IOException("删除队列和目录失败呢 baseDir=" +baseDir.getAbsolutePath());
        }
    }

    //检测队列的目录和文件是否存在
    public boolean checkFileExists(String queueName){
       File queueDataFile = new File(getQueueDataPath(queueName));
       boolean ok1 = queueDataFile.exists();
       if( !ok1){
           return false;
       }

       File queueStatFile  = new File(getQueueStatPath(queueName));
       boolean ok2 = queueStatFile.exists();
       if(!ok2){
           return false;
       }
       return true;
    }

    //下面这个方法是把消息写入到文件中
    /*
    1.检查要写入的文件是否存在
    2.把Message对象进行序列化，转成二进制字节数组
    3.获取到队列数据文件的长度
    4.计算出Message对象的offsetBeg和offsetEnd，来看看从队列的哪一个位置写入对象
    5.开始将消息写入到队列，追加写(true)
    6.先写消息长度
    7.再写消息本体
    8.最后通过stat更新消息的统计文件
     */
    public void sendMessage(MSGQueue queue, Message message) throws MqException, IOException {
        File baseDir = new File(getQueueDir(queue.getName()));
        if(!baseDir.exists()){
            throw new MqException("该队列对应的文件不存在！ baseDir=" + baseDir.getAbsolutePath());
        }

        byte[] messageBinary = BinaryTool.toBytes(message);
       synchronized (queue){

           File queueDataFile =new File(getQueueDataPath(queue.getName()));
           message.setOffsetBeg(queueDataFile.length() +4);
           message.setOffsetEnd(queueDataFile.length() + 4 + messageBinary.length);
           try(OutputStream outputStream = new FileOutputStream(queueDataFile.getName(),true)){
               try(DataOutputStream dataOutputStream = new DataOutputStream(outputStream)){
                   dataOutputStream.writeInt(messageBinary.length);
                   dataOutputStream.write(messageBinary);
               }
           }
           Stat stat = readStat(queue.getName());
           stat.totalCount += 1;
           stat.validCount += 1;
           writeStat(queue.getName(),stat);
       }

    }




}
